%训练并测试
%第一个卷积层的权重
W1 = randn(9, 9, 20);
%学习率
alpha = 0.001;
%训练集文件夹路径
train_path = 'D:\CNN\MNIST数据集\mnist0\traindata';  
%第一个隐层的权重
W2 = (2 * rand(100, 2000) - 1) / 20;
%输出层的连接权重
W3 = (2 * rand(10, 100) - 1) / 10;
%设置迭代次数
n = 1;
%初始化样本标签数据（一共有60000张图片，所以需要保存60000个标签数据）
label = zeros(60000, 1);
 
%先对每个样本进行标记
for i = 0:9
    trainfolderpath = strcat(train_path, num2str(i)); 
    % 获取文件夹中的所有图像文件
    trainimageFiles = dir(fullfile(trainfolderpath, '*.png'));
    % 循环读取每个图像
    for j = 1:length(trainimageFiles)
        % 假设imageFiles(j).name包含文件名
        FileName = trainimageFiles(j).name;
        % 利用fileparts获得文件名，方便后续辨识每张图片的标签
        [~, name_number, ~] = fileparts(FileName);
        %通过解析文件名就可以获得图片的序号
        number = str2num(name_number);
        %给图片打上标签
        label(number) = i;
    end
end
 
%开始计时
tic;
 
%训练网络时用到的文件夹，在该文件夹下将所有类别的图片混合，可以提高训练效果
Path_mix = 'train_mix/';  
%每张图片的交叉熵
loss = zeros(60000, 1);
%平均交叉熵
loss_ave = zeros(60000, 1);
%训练集的准确率
acc_train = zeros(60000, 1);
accuracy = 0;
%开始训练，只训练一轮
for epochs = 1:n
    folderpath =  Path_mix;
    %获取文件夹中的所有图像文件
    imageFiles = dir(fullfile(folderpath, '*.png'));  % 可以更改文件扩展名以匹配你的图像格式
    % 循环读取每个图像
    for j = 1:length(imageFiles)
        % 构建完整的文件路径
        lab = label(j); 
        %设置目标输出
        D = zeros(10, 1);
        D(lab+1) = 1;
        %获取要读取的图片的地址
        imagePath = strcat(folderpath, num2str(j));
        imagePath = strcat(imagePath, '.png');
        %输出正在处理的图像和训练进度
        fprintf('模型训练的进度：%f%%\n', j/600);
        s = sprintf('正在训练的图片为：%s', imagePath);
        disp(s);
        % 使用 imread 读取图像
        imageData = imread(imagePath);
        %对图像进行归一化操作
        imageData = round(imageData / 255);
        [W1, W2, W3, error, accuracy] = BP(W1, W2, W3, alpha, D, imageData, accuracy);
        acc_train(j) = accuracy / j;
        loss(j) = error;
        loss_ave(j) = sum(loss(1:j)) / j;
    end
end
% 停止计时
elapsedTime = toc;
% 打印执行时间
fprintf('训练时长为：%.4f 秒\n', elapsedTime);
%训练结束的信号
disp('训练结束，正在进行测试...');
%测试集地址
testPath = 'MNIST_test/'; 
%记录测试集有多少张图片
image_numbers_test = 1;
%测试集上的准确率
acc = 0;
for i = 0:9
    %依次遍历MINIST_train（训练集）文件夹下的每一个子文件夹，每一个子文件夹中包含相同的数字
    test_path = strcat(testPath, num2str(i)); 
    % 获取文件夹中的所有图像文件
    testimageFiles = dir(fullfile(test_path, '*.png'));
    % 循环读取每个图像
    for j = 1:length(testimageFiles)
        %测试集中每张图片的路径
        imagePath = fullfile(test_path, testimageFiles(j).name);
        %读取图像
        imageData = imread(imagePath);
        %图片归一化操作
        imageData = round(imageData / 255);
        %卷积
        img_conv1 = zeros(20, 20, 20);
        for k = 1:20
            img_conv1(:, :, k) = filter2(W1(:, :, k), imageData, 'valid'); 
        end    
        %对卷积得到的图片进行ReLU激活
        img_act = max(0, img_conv1);    
        %平均池化（2*2）
        img_pool = (img_act(1:2:end, 1:2:end, :) + img_act(2:2:end, 2:2:end, :) + img_act(1:2:end, 2:2:end, :) +img_act(2:2:end, 1:2:end, :)) / 4;
        %将img_pool转换成列向量（2000*1)
        img_input = reshape(img_pool, [], 1);
        %第一个隐层的输出
        v1 = W2 * img_input;
        y1 = max(0, v1);
        %输出层的输出
        v2 = W3 * y1;
        y2 = softmax(v2);
        [~, index] = max(y2);
        %如果分类正确准确率就加1
        acc = acc + ((index-1) == i);
        image_numbers_test = image_numbers_test + 1;
    end
end
%画图观察模型训练效果
subplot(1, 2, 1);
plot(1:60000, loss_ave);
xlabel('训练的图片数');
ylabel('平均损失函数值');
title('平均交叉熵随训练的图片数量的变化');
subplot(1, 2, 2);
plot(1:60000, acc_train);
xlabel('训练的图片数');
ylabel('准确率');
title('准确率随训练的图片数量的变化');
fprintf('测试集的准确率为%f\n', acc/(image_numbers_test-1));